﻿using Newtonsoft.Json;
using StackExchange.Redis;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Channels;
using System.Threading.Tasks;

namespace RedisDemo.Redis.Base
{
    public class RedisConfig
    {
        private readonly RedisOption _Option;
        private readonly ConcurrentDictionary<string, ConnectionMultiplexer> _connections;

        public RedisConfig(RedisOption Option)
        {
            _Option = Option;
            _connections = new ConcurrentDictionary<string, ConnectionMultiplexer>();
        }
        private static IDatabase db;
        private static ISubscriber sub;



        /// <summary>
        /// 创建连接
        /// </summary>
        /// <returns></returns>
        private ConnectionMultiplexer GetConnection()
        {
            return _connections.GetOrAdd(_Option.InstanceName,
                p => ConnectionMultiplexer.Connect(_Option.Connection));
        }

        public IDatabase GetDatabase()
        {
            if (db == null)
                return db = GetConnection().GetDatabase(_Option.DefaultDb);
            else
                return db;
        }

        public ISubscriber GetSubscriber()
        {
            if (sub == null)
                return sub = GetConnection().GetSubscriber();
            else
                return sub;
        }



        #region 同步使用
        /// <summary>
        /// 设置Key的过期时间
        /// </summary>
        /// <param name="key"></param>
        /// <param name="time"></param>
        /// <returns></returns>
        public bool Expire(string key, TimeSpan time)
        {
            try
            {
                GetDatabase().KeyExpire(key, time);
                return true;
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                return false;
            }
        }

        public TimeSpan GetExpire(string key)
        {
            try
            {
                // ReSharper disable once PossibleInvalidOperationException
                TimeSpan time = GetDatabase().KeyTimeToLive(key).Value;
                return time;
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
        }


        /// <summary>
        /// 写入缓存
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool Set(string key, RedisValue value)
        {
            bool result = false;
            try
            {
                result = GetDatabase().StringSet(key, value);
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
            return result;
        }

        /// <summary>
        /// 写入缓存并设置过期时间
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="expireTime"></param>
        /// <returns></returns>
        public bool Set(string key, string value, TimeSpan expireTime)
        {
            Console.WriteLine($"过期时间:{expireTime}");
            bool result = false;
            try
            {
                result = GetDatabase().StringSet(key, value);
                GetDatabase().KeyExpire(key, expireTime);
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }

            return result;
        }

        public RedisValue Get(string key)
        {
            return key == null ? "" : GetDatabase().StringGet(key);
        }

        /// <summary>
        /// 递增
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public long Incr(string key, long value)
        {
            return GetDatabase().StringIncrement(key, value);
        }

        public long Incr(string key)
        {
            return GetDatabase().StringIncrement(key);
        }

        /// <summary>
        /// 递减
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public long Decr(string key, long value)
        {
            return GetDatabase().StringIncrement(key, value);
        }
        public long Decr(string key)
        {
            return GetDatabase().StringDecrement(key);
        }

        public RedisValue Hget(string key, string item)
        {
            return GetDatabase().HashGet(key, item);
        }

        /// <summary>
        /// 加锁，如果锁定成功，就去执行方法
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="expire"></param>
        /// <returns></returns>
        public bool TryGetLock(string key, string value, TimeSpan expire)
        {
            return GetDatabase().LockTake(key, value, expire);
        }

        /// <summary>
        /// 解锁
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool LockRelease(string key, string value)
        {
            return GetDatabase().LockRelease(key, value);
        }

        /// <summary>
        /// 清空缓存
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool Remove(string key)
        {
            return GetDatabase().KeyDelete(key);
        }
        #endregion

        #region 异步使用

        /// <summary>
        /// 异步添加
        /// </summary>
        /// <param name="key">Redis Key</param>
        /// <param name="value">保存的值</param>
        /// <param name="expiry">过期时间</param>
        /// <returns></returns>
        public async Task<bool> StringSetAsync(string key, string value, TimeSpan? expiry = default(TimeSpan?))
        {
            return await GetDatabase().StringSetAsync(key, value, expiry);
        }

        /// <summary>
        /// 保存多个key value
        /// </summary>
        /// <param name="keyValues">键值对</param>
        /// <returns></returns>
        public async Task<bool> StringSetAsync(List<KeyValuePair<RedisKey, RedisValue>> keyValues)
        {

            List<KeyValuePair<RedisKey, RedisValue>> newkeyValues =
                keyValues.Select(p => new KeyValuePair<RedisKey, RedisValue>(p.Key, p.Value)).ToList();
            return await GetDatabase().StringSetAsync(newkeyValues.ToArray());
        }

        /// <summary>
        /// 获取单个key的值
        /// </summary>
        /// <param name="key">Redis Key</param>
        /// <returns></returns>
        public async Task<string> StringGetAsync(string key)
        {
            return await GetDatabase().StringGetAsync(key);
        }

        /// <summary>
        /// 获取多个Key
        /// </summary>
        /// <param name="listKey">>Redis Key集合</param>
        /// <returns></returns>
        public async Task<RedisValue[]> StringGetAsync(List<string> listKey)
        {
            return await GetDatabase().StringGetAsync(ConvertRedisKeys(listKey));
        }

        /// <summary>
        /// 批量删除
        /// </summary>
        /// <param name="listKey"></param>
        /// <returns></returns>
        public async Task<long> KeyDeleteAsync(List<string> listKey)
        {
            return await GetDatabase().KeyDeleteAsync(ConvertRedisKeys(listKey));
        }

        /// <summary>
        /// 重命名Key
        /// </summary>
        /// <param name="key"></param>
        /// <param name="newKey"></param>
        /// <returns></returns>
        public async Task<bool> KeyRenameAsync(string key, string newKey)
        {
            return await GetDatabase().KeyRenameAsync(key, newKey);
        }


        #region Hash操作

        /// <summary>
        ///  判断某个数据是否已经被缓存
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <returns></returns>
        public async Task<bool> HashExistsAsync(string key, string dataKey)
        {
            return await GetDatabase().HashExistsAsync(key, dataKey);
        }

        /// <summary>
        /// 添加Hash
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <param name="t"></param>
        /// <returns></returns>
        public async Task<bool> HashSetAsync<T>(string key, string dataKey, T t)
        {
            string strJson = JsonConvert.SerializeObject(t);
            return await GetDatabase().HashSetAsync(key, dataKey, strJson);
        }

        /// <summary>
        /// 移除Hash中的某值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <returns></returns>
        public async Task<bool> HashDeleteAsync(string key, string dataKey)
        {
            return await GetDatabase().HashDeleteAsync(key, dataKey);
        }

        /// <summary>
        /// 移除hash中的多个值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataKeys"></param>
        /// <returns></returns>
        public async Task<long> HashDeleteAsync(string key, List<RedisValue> dataKeys)
        {
            return await GetDatabase().HashDeleteAsync(key, dataKeys.ToArray());
        }

        /// <summary>
        /// 从hash表获取数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <returns></returns>
        public async Task<T> HashGetAsync<T>(string key, string dataKey)
        {
            string value = await GetDatabase().HashGetAsync(key, dataKey);
            return ConvertObj<T>(value);
        }

        /// <summary>
        /// 获取Hash表字段名
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<List<string>> HashKeysAsync<T>(string key)
        {
            var list = new List<string>();
            foreach (var item in await GetDatabase().HashKeysAsync(key))
            {
                list.Add(item.ToString());
            }
            return list;
        }

        /// <summary>
        /// 获取Hash表全部
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<HashEntry[]> HashGetAllAsync<T>(string key)
        {
            return await GetDatabase().HashGetAllAsync(key);
        }

        #endregion

        #endregion

        #region List

        /// <summary>
        /// 移除指定ListId的内部List的值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public async Task<long> ListRemoveAsync<T>(string key, T value, long count = 0)
        {
            return await GetDatabase().ListRemoveAsync(key, ConvertJson(value), count);
        }

        /// <summary>
        /// 获取指定key的List
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<List<T>> ListRangeAsync<T>(string key)
        {
            var values = await GetDatabase().ListRangeAsync(key);
            return ConvetList<T>(values);
        }

        /// <summary>
        /// 入队
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public async Task<long> ListRightPushAsync<T>(string key, T value)
        {
            return await GetDatabase().ListRightPushAsync(key, ConvertJson(value));
        }

        /// <summary>
        /// 出队
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<T> ListRightPopAsync<T>(string key)
        {
            var value = await GetDatabase().ListRightPopAsync(key);
            return ConvertObj<T>(value);
        }

        /// <summary>
        /// 获取集合中的数量
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<long> ListLengthAsync(string key)
        {
            return await GetDatabase().ListLengthAsync(key);
        }

        #endregion

        #region SortedSet 有序集合

        /// <summary>
        /// 添加
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="score"></param>
        /// <returns></returns>
        public async Task<bool> SortedSetAddAsync<T>(string key, T value, double score)
        {
            return await GetDatabase().SortedSetAddAsync(key, ConvertJson<T>(value), score);
        }
        /// <summary>
        /// 删除
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public async Task<bool> SortedSetRemoveAsync<T>(string key, T value)
        {
            return await GetDatabase().SortedSetRemoveAsync(key, ConvertJson(value));
        }

        /// <summary>
        /// 获取全部
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<List<T>> SortedSetRangeByRankAsync<T>(string key)
        {
            var values = await GetDatabase().SortedSetRangeByRankAsync(key);
            return ConvetList<T>(values);
        }

        public async Task<long> SortedSetLengthAsync(string key)
        {
            return await GetDatabase().SortedSetLengthAsync(key);
        }


        #endregion


        #region 发布订阅

        /// <summary>
        ///  Redis发布订阅  订阅
        /// </summary>
        /// <param name="subChannel"></param>
        /// <param name="handler"></param>
        /// <returns></returns>
        public async Task SubscribeAsync(string subChannel, Action<RedisChannel, RedisValue> handler = null)
        {
            await GetSubscriber().SubscribeAsync(subChannel, (Channel, message) =>
             {
                 if (handler == null)
                 {
                     Console.WriteLine(subChannel + " 订阅收到消息：" + message);
                 }
                 else
                 {
                     handler(Channel, message);
                 }
             });
        }

        /// <summary>
        /// Redis发布订阅  发布
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="channel"></param>
        /// <param name="msg"></param>
        /// <returns></returns>
        public async Task<long> PublishAsync<T>(string channel, T msg)
        {
            return await GetSubscriber().PublishAsync(channel, ConvertJson(msg));
        }

        /// <summary>
        /// 取消订阅
        /// </summary>
        /// <param name="channel"></param>
        /// <returns></returns>
        public async Task UnsubscribeAsync(string channel)
        {
            await GetSubscriber().UnsubscribeAsync(channel);
        }

        /// <summary>
        /// Redis发布订阅  取消全部订阅
        /// </summary>
        /// <returns></returns>
        public async Task UnsubscribeAllAsync()
        {
            await GetSubscriber().UnsubscribeAllAsync();
        }

        #endregion




        #region 辅助方法
        private RedisKey[] ConvertRedisKeys(List<string> redisKeys)
        {
            return redisKeys.Select(redisKey => (RedisKey)redisKey).ToArray();
        }
        private static string ConvertJson<T>(T value)
        {
            string result = value is string ? value.ToString() : JsonConvert.SerializeObject(value);
            return result;
        }

        private List<T> ConvetList<T>(RedisValue[] values)
        {
            List<T> result = new List<T>();
            foreach (var item in values)
            {
                var model = ConvertObj<T>(item);
                result.Add(model);
            }
            return result;
        }

        private T ConvertObj<T>(RedisValue value)
        {
            return JsonConvert.DeserializeObject<T>(value);
        }

        #endregion



        /// <summary>
        /// 清空所有缓存
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        //public virtual void FlushAll()
        //{
        //   // GetDatabase().FlushAll();
        //}
    }
}
